/*
   This file is part of the 'Let's Do Stuff Together' project
   http://code.google.com/p/dostufftogether/

   Copyright 2010 Christoph Fuchs, Stefan Thaler

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package com.appspot.android2gather.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.net.ssl.HttpsURLConnection;

import oauth.signpost.signature.HmacSha1MessageSigner;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.client.params.CookiePolicy;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.widget.Toast;

import com.appspot.android2gather.LetsDoStuff;
import com.appspot.android2gather.LogonWebView;
import com.appspot.android2gather.activity.ErrorMessage;
import com.appspot.android2gather.activity.RequestParameters;
import com.appspot.android2gather.configuration.ServerConfiguration;
import com.facebook.android.DialogError;
import com.facebook.android.Facebook.DialogListener;
import com.facebook.android.FacebookError;
import com.google.gson.Gson;

/**
 * Helper class to query the activity server
 * 
 * @author Christoph 'Gigi' Fuchs
 * 
 */
public class QueryHelper {

	public static DefaultHttpClient defaultHttpClient = new DefaultHttpClient();

	static {
		defaultHttpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY,
				CookiePolicy.BROWSER_COMPATIBILITY);
	}
	/**
	 * http://code.google.com/apis/accounts/docs/OAuth_ref.html#AccessToken
	 * @return
	 */
	public static String getBuzzOAuthToken(Context context)
	{
		List<NameValuePair> queryParams = new ArrayList<NameValuePair>();
		queryParams.add(new BasicNameValuePair("oauth_consumer_key", "android2gather.appengine.com"));
		queryParams.add(new BasicNameValuePair("oauth_token", LetsDoStuff.BUZZ_OAUTH_TOKEN ));
		queryParams.add(new BasicNameValuePair("oauth_verifier", LetsDoStuff.BUZZ_VERIFIYER ));
		queryParams.add(new BasicNameValuePair("oauth_signature_method", "HMAC-SHA1"));
		queryParams.add(new BasicNameValuePair("oauth_timestamp", System.currentTimeMillis() / 1000 +""));
		queryParams.add(new BasicNameValuePair("oauth_nonce", Long.toString(new Random().nextLong()) +""));
		
		HmacSha1MessageSigner signer = new HmacSha1MessageSigner();
		
		HttpGet get = new HttpGet("https://www.google.com/accounts/OAuthGetAccessToken");
		
		
		try {
			URI requestUri = URIUtils.createURI("https",
					"", -1, "/accounts/OAuthGetAccessToken",
					URLEncodedUtils.format(queryParams, "UTF-8"), null);
			return queryServer(context, requestUri);
			
		}
		catch (Exception e) 
		{
			Log.e("getOAuthToken",getStrackTrace(e));
			return null;
		}
		
	}
	
	
	public static String postBuzzServer(Context context, URI requestUri, String body) {
		CookieSyncManager.createInstance(context);
		String cookie = CookieManager.getInstance().getCookie(
				"www.google.com");
		Log.i("QueryHelper", "Using cookie:" + cookie);
		try {
			Log.i("QueryHelper", "Encoding URL to utf-8...");
			HttpPost httpPost = new HttpPost(requestUri);
			httpPost.getParams().setParameter(ClientPNames.COOKIE_POLICY,
					CookiePolicy.BROWSER_COMPATIBILITY);
			//httpPost.setHeader("Authorization", );
			httpPost.setHeader("Content-Type","application/json");
			//httpPost.setHeader("Key","AIzaSyCISzorjQYRjr3SLCyZ7hq-cd5V3idYB0w");
			httpPost.setHeader("cookie", cookie);
			httpPost.setEntity( new StringEntity(body) );
			
			HmacSha1MessageSigner signer  = new HmacSha1MessageSigner();
			signer.setConsumerSecret("k0J+3q9FdBPxJWn++NjsTy7U");
			signer.setTokenSecret(getBuzzOAuthToken(context));
			
			

			Log.i("QueryHelper", "Requesting " + requestUri);
			HttpResponse response = defaultHttpClient.execute(httpPost);
			int statusCode = response.getStatusLine().getStatusCode();

			Log.i("QueryHelper", "Server response " + statusCode);

			if (statusCode == HttpStatus.SC_OK) {
				Log.i("QueryHelper", "Server responded with "
						+ HttpStatus.SC_OK + " (OK)");

				HttpEntity entity = response.getEntity();
				if (entity != null) {
					try {
						if (entity.getContentLength() > 0) {
							if (entity.getContentLength() > 0) {
								return convertStreamToString(entity.getContent());
							}
						}
					} finally {
						if (entity != null) {
							entity.consumeContent();
						}
					}

					Log.v("QueryHelper", "Response body: " + new String(body));

					return null;
				}
			}
		} catch (ClientProtocolException e) {
			Toast toast = Toast.makeText(context,
					"Could not connect to server. Reason: " + e.getMessage(),
					Toast.LENGTH_SHORT);
			toast.show();
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			Toast toast = Toast.makeText(context, "Encoding error. Reason: "
					+ e.getMessage(), Toast.LENGTH_SHORT);
			toast.show();
			e.printStackTrace();
		} catch (IOException e) {
			Toast toast = Toast.makeText(context,
					"I/O error. Reason: " + e.getMessage(), Toast.LENGTH_SHORT);
			toast.show();
			e.printStackTrace();
		}

		return null;
	}

	/**
	 * Query the activity server for activities near you. Using HTTP GET.
	 * 
	 * @param context
	 *            the activity context
	 * @param httpGetRequestURL
	 *            query string
	 * 
	 * @return the response body. Depending on the query this is either the json
	 *         representation of the <code>Activity</code> instances or a simple
	 *         HTML message body. <code>null</code> if unsuccessful
	 */
	public static String postServer(Context context, URI requestUri) {
		CookieSyncManager.createInstance(context);
		String cookie = CookieManager.getInstance().getCookie(
				ServerConfiguration.ACTIVITY_SERVER);
		Log.i("QueryHelper", "Using cookie:" + cookie);
		try {
			Log.i("QueryHelper", "Encoding URL to utf-8...");
			HttpPost httpGet = new HttpPost(requestUri);
			httpGet.getParams().setParameter(ClientPNames.COOKIE_POLICY,
					CookiePolicy.BROWSER_COMPATIBILITY);
			httpGet.setHeader("cookie", cookie);

			Log.i("QueryHelper", "Requesting " + requestUri);
			HttpResponse response = defaultHttpClient.execute(httpGet);
			int statusCode = response.getStatusLine().getStatusCode();

			Log.i("QueryHelper", "Server response " + statusCode);

			if (statusCode == HttpStatus.SC_OK) {
				Log.i("QueryHelper", "Server responded with "
						+ HttpStatus.SC_OK + " (OK)");

				HttpEntity entity = response.getEntity();
				byte[] body = null;
				if (entity != null) {
					try {
						if (entity.getContentLength() > 0) {
							if (entity.getContentLength() > 0) {
								return convertStreamToString(entity.getContent());
							}
						}
					} finally {
						if (entity != null) {
							entity.consumeContent();
						}
					}

					Log.v("QueryHelper", "Response body: " + new String(body));

					return null;
				}
			}
		} catch (ClientProtocolException e) {
			Toast toast = Toast.makeText(context,
					"Could not connect to server. Reason: " + e.getMessage(),
					Toast.LENGTH_SHORT);
			toast.show();
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			Toast toast = Toast.makeText(context, "Encoding error. Reason: "
					+ e.getMessage(), Toast.LENGTH_SHORT);
			toast.show();
			e.printStackTrace();
		} catch (IOException e) {
			Toast toast = Toast.makeText(context,
					"I/O error. Reason: " + e.getMessage(), Toast.LENGTH_SHORT);
			toast.show();
			e.printStackTrace();
		}

		return null;
	}

	public static String convertStreamToString(InputStream is)
			throws IOException {
		if (is != null) {
			Writer writer = new StringWriter();

			char[] buffer = new char[1024];
			try {
				Reader reader = new BufferedReader(new InputStreamReader(is,
						"UTF-8"));
				int n;
				while ((n = reader.read(buffer)) != -1) {
					Log.i("Convert", "Writing "+n+" bytes" );
					Log.i("Convert", new String(buffer) );
					writer.write(buffer, 0, n);
				}
			} finally {
				is.close();
			}
			return writer.toString();
		} else {
			return "";
		}
	}

	public static String queryServer(Context context, URI httpGetRequestURL) {
		CookieSyncManager.createInstance(context);
		String cookie = CookieManager.getInstance().getCookie(
				ServerConfiguration.ACTIVITY_SERVER);
		Log.i("QueryHelper", "Using cookie:" + cookie);
		try {
			Log.i("QueryHelper", "Encoding URL to utf-8...");
			HttpGet httpGet = new HttpGet(httpGetRequestURL);
			httpGet.getParams().setParameter(ClientPNames.COOKIE_POLICY,
					CookiePolicy.BROWSER_COMPATIBILITY);
			httpGet.setHeader("cookie", cookie);

			Log.i("QueryHelper", "Requesting " + httpGetRequestURL);
			HttpResponse response = defaultHttpClient.execute(httpGet);
			int statusCode = response.getStatusLine().getStatusCode();

			Log.i("QueryHelper", "Server response " + statusCode);

			if (statusCode == HttpStatus.SC_OK) {
				Log.i("QueryHelper", "Server responded with "
						+ HttpStatus.SC_OK + " (OK)");

				HttpEntity entity = response.getEntity();
				byte[] body = null;
				if (entity != null) {
					try {
						Log.i("GET","Recieved "+entity.getContentLength()+" bytes");
						if (entity.getContentLength() > 0) {
							return convertStreamToString(entity.getContent());
						}
					} finally {
						if (entity != null) {
							entity.consumeContent();
						}
					}

					Log.v("QueryHelper", "Response body: " + new String(body));

					return null;
				}
			}
		} catch (ClientProtocolException e) {
			Toast toast = Toast.makeText(context,
					"Could not connect to server. Reason: " + e.getMessage(),
					Toast.LENGTH_SHORT);
			toast.show();
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			Toast toast = Toast.makeText(context, "Encoding error. Reason: "
					+ e.getMessage(), Toast.LENGTH_SHORT);
			toast.show();
			e.printStackTrace();
		} catch (IOException e) {
			Toast toast = Toast.makeText(context,
					"I/O error. Reason: " + e.getMessage(), Toast.LENGTH_SHORT);
			toast.show();
			e.printStackTrace();
		}

		return null;
	}

	/**
	 * Checks whether this user is logged in.
	 * 
	 * @return
	 * @throws URISyntaxException
	 */
	public static boolean isLoggedIn(Context context) {
		try {
			String response = queryServer(context, URIUtils.createURI("http",
					ServerConfiguration.ACTIVITY_SERVER, -1, "/logon",
					"check=true", null));

			if ("true".equals(response)) {
				Log.i("isLoggedIn", "User is logged in.");
				return true;
			}
			if ("false".equals(response)) {
				Log.i("isLoggedIn", "User is not logged in.");
				return false;
			}
			try {
				ErrorMessage fromJson = new Gson().fromJson(response,
						ErrorMessage.class);
				Toast toast = Toast.makeText(context,
						"Check loginstatus failed: " + fromJson.getM(),
						Toast.LENGTH_SHORT);
				toast.show();
				Log.e("isLoggedIn",
						"Check loginstatus failed:" + fromJson.getM());
			} catch (Exception e) {
				Log.e("isLoggedIn", getStrackTrace(e));
			}
			return false;
		} catch (Exception e) {
			Log.e("isLoggedIn", getStrackTrace(e));
			return false;
		}
	}

	/**
	 * Converts a JSON Server message to the object of the given class. Displays
	 * and logs an error message.
	 * 
	 * @param <T>
	 * @param responseType
	 * @param response
	 * @return
	 */
	public static <T> T parseResponse(Class<T> responseType,
			String responseString, Context context) {
		T responseObject;
		Gson gson = new Gson();
		try {

			responseObject = gson.fromJson(responseString, (Type) responseType);
			return responseObject;
		} catch (Exception e) {
			Log.e("Parse error", QueryHelper.getStrackTrace(e));
			try {
				ErrorMessage fromJson = new Gson().fromJson(responseString,
						ErrorMessage.class);
				Toast toast = Toast.makeText(context, "Server error: "
						+ fromJson.getM(), Toast.LENGTH_SHORT);
				toast.show();
				Log.e(context.getPackageName(),
						"Server error:" + fromJson.getM());
			} catch (Exception e2) {
				Log.e(context.getPackageName(), QueryHelper.getStrackTrace(e2));
			}
		}
		return null;

	}

	public static boolean attendActivity(String id, Context context) {

		List<NameValuePair> queryParams = new ArrayList<NameValuePair>();
		queryParams.add(new BasicNameValuePair("m", "post"));
		queryParams.add(new BasicNameValuePair(RequestParameters.ID, id));
		try {
			URI requestUri = URIUtils.createURI("http",
					ServerConfiguration.ACTIVITY_SERVER, -1, "/attend",
					URLEncodedUtils.format(queryParams, "UTF-8"), null);

			String response = queryServer(context, requestUri);

			if ("true".equals(response)) {
				Log.i("attend", "Usser is attending event" + id);
				return true;
			}
			try {
				ErrorMessage fromJson = new Gson().fromJson(response,
						ErrorMessage.class);
				Toast toast = Toast.makeText(context, "Attending " + id
						+ " failed." + fromJson.getM(), Toast.LENGTH_SHORT);
				toast.show();
				Log.e("attend",
						"Attending " + id + " failed." + fromJson.getM());
			} catch (Exception e) {
				Log.e("attend", getStrackTrace(e));
			}
			return false;
		} catch (URISyntaxException e) {
			Log.e("attendActivity", QueryHelper.getStrackTrace(e));
			return false;
		}
	}

	public static boolean attendActivity(String activityId, String personEmail,
			Context context) {

		List<NameValuePair> queryParams = new ArrayList<NameValuePair>();
		queryParams.add(new BasicNameValuePair("m", "post"));
		queryParams.add(new BasicNameValuePair(RequestParameters.EMAIL,
				personEmail));
		queryParams
				.add(new BasicNameValuePair(RequestParameters.ID, activityId));
		try {
			URI requestUri = URIUtils.createURI("http",
					ServerConfiguration.ACTIVITY_SERVER, -1, "/attend",
					URLEncodedUtils.format(queryParams, "UTF-8"), null);

			String response = queryServer(context, requestUri);

			if ("true".equals(response)) {
				Log.i("attend", "Usser is attending event" + activityId);
				return true;
			}
			try {
				ErrorMessage fromJson = new Gson().fromJson(response,
						ErrorMessage.class);
				Toast toast = Toast.makeText(context, "Attending " + activityId
						+ " failed." + fromJson.getM(), Toast.LENGTH_SHORT);
				toast.show();
				Log.e("attend", "Attending " + activityId + " failed."
						+ fromJson.getM());
			} catch (Exception e) {
				Log.e("attend", getStrackTrace(e));
			}
			return false;
		} catch (URISyntaxException e) {
			Log.e("attendActivity", QueryHelper.getStrackTrace(e));
			return false;
		}
	}

	/**
	 * Pull RSVP from event and update attendees
	 * 
	 * @param facebookEventId
	 */
	public static void pullFacebookRSVPs(final String activityId,
			final String facebookEventId, final Context context) {
		Log.i("PullFacebook", "Attempting to pull RSVP");
		// if (LetsDoStuff.facebook.isSessionValid()) { //TODO chekc whether
		// somebody is logged on
		Log.i("PullFacebook", "SessionValid");
		
		LetsDoStuff.facebook.authorize(LetsDoStuff.letsDoStuff,
				ServerConfiguration.permissions, new DialogListener() {

					@Override
					public void onComplete(Bundle values) {

						String response = null;
						try {
							response = LetsDoStuff.facebook.request(
									facebookEventId + "/invited", values);
							RSVPData rsvps = new Gson().fromJson(response,
									RSVPData.class);
							if (rsvps!=null && rsvps.getData()!=null) 
							{
								for (final RSVP r : rsvps.getData()) {
									if (r.isAttending()) {
										attendActivity(activityId, r.getId()
												+ "@facebook.com", context);
										Log.i("PullFacebook", r.getName()
												+ " is Attending " + activityId);
									}

								}
							} else 
							{
								Log.i("Facebook","No Facebook event for this activity");
							}
							

						} catch (MalformedURLException e) {
							Log.e("pullFacebookRSVP", getStrackTrace(e));
						} catch (IOException e) {
							Log.e("pullFacebookRSVP", getStrackTrace(e));
						}

						Log.i("PULL rsvp", response);
					}

					@Override
					public void onFacebookError(FacebookError e) {
						Log.e("Facebook Event", QueryHelper.getStrackTrace(e));
					}

					@Override
					public void onError(DialogError e) {
						Log.e("Facebook Event", QueryHelper.getStrackTrace(e));
					}

					@Override
					public void onCancel() {
						Log.e("Facebook Event", "Cancelled");
					}

				});
		// }
	}

	/**
	 * Converts a <code>Stacktrace</code> to a <code>String</code>. TODO
	 * refactor
	 * 
	 * @param t
	 * @return the Stacktrace as a <code>String</code>
	 */
	public static String getStrackTrace(Throwable t) {
		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw, true);
		t.printStackTrace(pw);
		pw.flush();
		sw.flush();
		return sw.toString();
	}

	/**
	 * Makes WebView load the page specified, which refreshes the last HTML
	 * loaded, and returns that.
	 * 
	 * @param url
	 * @return
	 */
	public static String getHtml(String url) {
		LogonWebView.webview.loadUrl(url);
		return LogonWebView.getLastHTML();
	}

	/**
	 * Refreshes LetsDoStuff.isLoggedIn by sending the check request via the
	 * WebView.
	 * 
	 * @return the freshly determined login status
	 */
	public static boolean refreshLoggedInStatus() {
		String html = getHtml(ServerConfiguration.ACTIVITY_SERVER
				+ "logon?check=true");
		if (html.contains("<head><head></head><body>true</body></head>")) {
			Log.v("WebView", "AUTHENTICATED");
			LetsDoStuff.isLoggedIn = true;
		} else
			LetsDoStuff.isLoggedIn = false;
		return LetsDoStuff.isLoggedIn;
	}

	/**
	 * Pushes a message to Google Buzz under the currently authenticated user's account.
	 * @param message the message to post
	 * @author Stefan Thaler
	 */
	public static void shareOnBuzz(String message) {
		try {
			//TODO put in a method with text argument, call when creating activity, remove main menu button.
			//push a hello world message to google buzz.
			BuzzPostData bpd = new BuzzPostData();
			BuzzObject bo = new BuzzObject();
			bo.setContent(message);
			bo.setType("note");
			bpd.setObject(bo);
			BuzzPost bp = new BuzzPost();
			bp.setData(bpd);

			String json = new Gson().toJson(bp);



			LetsDoStuff.provider.retrieveAccessToken(LetsDoStuff.BUZZ_VERIFIYER);

			Log.i("Oauth","Access token: " + LetsDoStuff.consumer.getToken());
			Log.i("Oauth","Token secret: " + LetsDoStuff.consumer.getTokenSecret());


			URL url = new URL("https://www.googleapis.com/buzz/v1/activities/@me/@self");
			HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
			con.setRequestMethod("POST");
			con.setRequestProperty( "Content-Length", String.valueOf( json.length() ) );
			con.setRequestProperty("Content-Type", "application/json" );
			con.setDoOutput( true );
			LetsDoStuff.consumer.sign(con );  

			//sending request
			OutputStream outStream = con.getOutputStream();
			outStream.write( json.getBytes() );
			outStream.flush();
			outStream.close();

			con.connect();

			Log.i("Response: " ,  con.getResponseCode() + " "
					+ con.getResponseMessage());

			Log.i("ResponseBody:",QueryHelper.convertStreamToString(con.getInputStream()));



		} catch (Exception e) {
			Log.e("Exception",QueryHelper.getStrackTrace(e));
		}

	}
}

class RSVPData {
	private RSVP[] data;

	public void setData(RSVP[] data) {
		this.data = data;
	}

	public RSVP[] getData() {
		return data;
	}
}

class RSVP {
	private String name;
	private String id;
	private String rsvp_status;

	public boolean isAttending() {
		return "attending".equals(getRsvp_status());
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getId() {
		return id;
	}

	public void setRsvp_status(String rsvp_status) {
		this.rsvp_status = rsvp_status;
	}

	public String getRsvp_status() {
		return rsvp_status;
	}
}






